/*              Copyright(c) 2008 Dave Sexton              *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.ComponentModel.Design;
using Microsoft.Ddue.Tools;

namespace DaveSexton.Sandcastle
{
	[Editor(typeof(ResolveExternalLinksComponent.Editor), typeof(UITypeEditor))]
	[TypeConverter(typeof(ResolveExternalLinksComponent.Converter))]
	partial class ResolveExternalLinksComponent
	{
		#region Private / Protected
		private readonly ResolveExternalLinksComponentConfiguration __config;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ResolveExternalLinksComponent" /> class.
		/// </summary>
		public ResolveExternalLinksComponent(BuildAssembler assembler, XPathNavigator configuration)
			: base(assembler, configuration)
		{
			__config = ResolveExternalLinksComponentConfiguration.GetConfiguration(configuration);

			WriteMessage(MessageLevel.Info, string.Format(System.Globalization.CultureInfo.CurrentCulture,
				Resources.Text.LoadedScopesAndMappings, __config.MappingScopesCount, __config.MappingCount));
		}
		#endregion

		#region Methods
		public override void Apply(XmlDocument document)
		{
			Apply(document, null);
		}

		public override void Apply(XmlDocument document, string key)
		{
			Apply(document, key, __config);
		}

		/// <summary>
		/// Configuration method that is required for the component to be editable in the
		/// <a href="http://www.codeplex.com/SHFB">Sandcastle Help File Builder</a>.
		/// </summary>
		public static string ConfigureComponent(string currentConfig)
		{
			XmlDocument config = new XmlDocument();
			config.PreserveWhitespace = true;
			config.LoadXml(currentConfig);

			XmlNode component = config.DocumentElement;

			Editor editor = new Editor();

			if ((bool) editor.EditValue(null, component))
				return component.OuterXml;
			else
				return currentConfig;
		}
		#endregion

		#region Nested
		/// <summary>
		/// Editor class that is required for the component to be editable in 
		/// <a href="http://www.codeplex.com/DocProject">DocProject</a>.
		/// </summary>
		private sealed class Editor : UITypeEditor
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="Editor" /> class.
			/// </summary>
			public Editor()
			{
			}
			#endregion

			#region Methods
			public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
			{
				// DocProject supports a value of DropDown as well, although this implementation uses a modal dialog
				return UITypeEditorEditStyle.Modal;
			}

			public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
			{
				IXPathNavigable xml = value as IXPathNavigable;

				ResolveExternalLinksComponentConfiguration config = ResolveExternalLinksComponentConfiguration.GetConfiguration(provider ?? context, xml);

				using (Form form = new Form())
				{
					form.ShowIcon = false;
					form.ShowInTaskbar = false;
					form.MinimizeBox = false;
					form.MaximizeBox = false;
					form.StartPosition = FormStartPosition.CenterParent;

					using (ResolveExternalLinksComponentEditorControl control = new ResolveExternalLinksComponentEditorControl(
						ResolveExternalLinksComponentConfiguration.GetDisplayName(context, provider, config), form, config))
					{
						control.Dock = DockStyle.Fill;

						form.Controls.Add(control);

						if (ResolveExternalLinksComponentHost.ShowDialog(form, config.Host) == DialogResult.OK)
							return true;	// success - host should persist the changes to the XML configuration
						else
							return false;	// canceled by user
					}
				}
			}
			#endregion
		}

		/// <summary>
		/// Converts the <see cref="ResolveExternalLinksComponent"/> to and from various other types, 
		/// such as <see cref="System.String"/>, and optionally provides dynamic properties 
		/// that can be edited in the <a href="http://www.codeplex.com/DocProject">DocProject</a>
		/// properties window.
		/// </summary>
		private sealed class Converter : TypeConverter
		{
			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="Converter" /> class.
			/// </summary>
			public Converter()
			{
			}
			#endregion

			#region Methods
			public override bool GetPropertiesSupported(ITypeDescriptorContext context)
			{
				return ResolveExternalLinksComponentConfiguration.PropertiesSupported;
			}

			public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
			{
				IXPathNavigable xml = value as IXPathNavigable;

				ResolveExternalLinksComponentConfiguration configuration = ResolveExternalLinksComponentConfiguration.GetConfiguration(context, xml);

				PropertyDescriptorCollection properties = new PropertyDescriptorCollection(null);

				foreach (PropertyDescriptor realProperty in TypeDescriptor.GetProperties(configuration, attributes))
				{
					properties.Add(new Property(realProperty, configuration));
				}

				return properties;
			}

			public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
			{
				if (destinationType == typeof(string))
				{
					IXPathNavigable xml = value as IXPathNavigable;

					if (xml != null)
						return ResolveExternalLinksComponentConfiguration.GetDisplayValue(context, culture, xml);
				}

				return base.ConvertTo(context, culture, value, destinationType);
			}
			#endregion
		}

		private sealed class Property : PropertyDescriptor
		{
			#region Public Properties
			public override Type ComponentType
			{
				get
				{
					return realProperty.ComponentType;
				}
			}

			public override bool IsReadOnly
			{
				get
				{
					return realProperty.IsReadOnly;
				}
			}

			public override Type PropertyType
			{
				get
				{
					return realProperty.PropertyType;
				}
			}
			#endregion

			#region Private / Protected
			private readonly PropertyDescriptor realProperty;
			private readonly ResolveExternalLinksComponentConfiguration configuration;
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="Property" /> class.
			/// </summary>
			public Property(PropertyDescriptor realProperty, ResolveExternalLinksComponentConfiguration configuration)
				: base(realProperty, new Attribute[] { new NotifyParentPropertyAttribute(true) })
			{
				this.realProperty = realProperty;
				this.configuration = configuration;
			}
			#endregion

			#region Methods
			public override bool CanResetValue(object component)
			{
				return realProperty.CanResetValue(configuration);
			}

			public override object GetValue(object component)
			{
				return realProperty.GetValue(configuration);
			}

			public override void ResetValue(object component)
			{
				realProperty.ResetValue(configuration);
			}

			public override void SetValue(object component, object value)
			{
				realProperty.SetValue(configuration, value);
			}

			public override bool ShouldSerializeValue(object component)
			{
				return realProperty.ShouldSerializeValue(configuration);
			}
			#endregion
		}
		#endregion
	}
}
